# ~*~ coding: utf-8 ~*~
import numbers
from vine.five import string_t
from collections import Iterable

from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import InputRequired, ValidationError
from celery.schedules import crontab_parser, CRON_INVALID_TYPE, CRON_PATTERN_INVALID


from apps.common.form import ErrorFormMinx

__all__ = ['CrontabForm']


class CrontabForm(ErrorFormMinx, FlaskForm):
    minute = StringField(validators=[InputRequired(message="分不能为空")])
    hour = StringField(validators=[InputRequired(message="时不能为空")])
    day_of_month = StringField(validators=[InputRequired(message="天不能为空")])
    month_of_year = StringField(validators=[InputRequired(message="月不能为空")])
    day_of_week = StringField(validators=[InputRequired(message="周不能为空")])

    @property
    def data(self):
        return {
            'minute': self.minute.data,
            'hour': self.hour.data,
            'day_of_month': self.day_of_month.data,
            'month_of_year': self.month_of_year.data,
            'day_of_week': self.day_of_week.data,
        }

    @staticmethod
    def expand_cronspec(cronspec, max_, min_=0):
        """ copy from celery.schedules.crontab._expand_cronspec """
        if isinstance(cronspec, numbers.Integral):
            result = {cronspec}
        elif isinstance(cronspec, string_t):
            result = crontab_parser(max_, min_).parse(cronspec)
        elif isinstance(cronspec, set):
            result = cronspec
        elif isinstance(cronspec, Iterable):
            result = set(cronspec)
        else:
            raise TypeError(CRON_INVALID_TYPE.format(type=type(cronspec)))

        # assure the result does not preceed the min or exceed the max
        for number in result:
            if number >= max_ + min_ or number < min_:
                raise ValueError(CRON_PATTERN_INVALID.format(
                    min=min_, max=max_ - 1 + min_, value=number))
        return result

    def validate_minute(self, field):
        minute = self.minute.data
        try:
            self.expand_cronspec(minute, 60)
        except TypeError:
            raise ValidationError('分钟的数字类型错误')
        except ValueError:
            raise ValidationError('分钟的数字范围错误')

    def validate_hour(self, field):
        hour = self.hour.data
        try:
            self.expand_cronspec(hour, 24)
        except TypeError:
            raise ValidationError('小时的数字类型错误')
        except ValueError:
            raise ValidationError('小时的数字范围错误')

    def validate_day_of_month(self, field):
        day_of_month = self.day_of_month.data
        try:
            self.expand_cronspec(day_of_month, 31)
        except TypeError:
            raise ValidationError('天的数字类型错误')
        except ValueError:
            raise ValidationError('天的数字范围错误')

    def validate_month_of_year(self, field):
        month_of_year = self.month_of_year.data
        try:
            self.expand_cronspec(month_of_year, 12)
        except TypeError:
            raise ValidationError('月的数字类型错误')
        except ValueError:
            raise ValidationError('月的数字范围错误')

    def validate_day_of_week(self, field):
        day_of_week = self.day_of_week.data
        try:
            self.expand_cronspec(day_of_week, 7)
        except TypeError:
            raise ValidationError('周的数字类型错误')
        except ValueError:
            raise ValidationError('周的数字范围错误')
